#include "jpg.h"

// 参数说明：
//   jpgdata: jpg图片数据
//   jpgsize: jpg图片大小
// 返回值说明：
//   成功：指向rgb数据的指针
//   失败：NULL

#define RGB_SIZE 3

void scale_image(const char *jpeg_buffer, int original_width, int original_height,
                 char *scaled_buffer, int scaled_width, int scaled_height) 
{
    // 检查缩放尺寸是否有效
    if (scaled_width <= 0 || scaled_height <= 0) {
        return;
    }

    float x_scale = (float)original_width / scaled_width;
    float y_scale = (float)original_height / scaled_height;
    printf("(%f,%f)",x_scale,y_scale);

    for (int y = 0; y < scaled_height; ++y) {
        for (int x = 0; x < scaled_width; ++x) {
            // 计算原始图像中对应的x和y坐标
            float orig_x = x * x_scale;
            float orig_y = y * y_scale;

            // 使用最近邻插值
            int ix = (int)orig_x;
            int iy = (int)orig_y;

            //确保坐标在原始图像范围内
            ix = ix < 0 ? 0 : (ix >= original_width ? original_width - 1 : ix);
            iy = iy < 0 ? 0 : (iy >= original_height ? original_height - 1 : iy);

            // 计算原始图像中当前像素的偏移量
            int offset = (iy * original_width + ix) * RGB_SIZE;

            // 将原始图像中的RGB值复制到缩放后的图像中
            int scaled_offset = (y * scaled_width + x) * RGB_SIZE;
            for (int c = 0; c < RGB_SIZE; ++c) {
                scaled_buffer[scaled_offset + c] = jpeg_buffer[offset + c];
            }
            
        }
    }
}
void jpg2rgb(const char *jpgdata, size_t jpgsize, struct img_info *jpginfo)
{
    // 1，声明解码结构体，以及错误管理结构体
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;

    // 2，使用缺省的出错处理来初始化解码结构体
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);

    // 3，配置该cinfo，使其从 jpgdata 中读取jpgsize个字节
    //    这些数据必须是完整的JPEG数据
    jpeg_mem_src(&cinfo, jpgdata, jpgsize);

    // 4，读取JPEG文件的头，并判断其格式是否合法
    if(!jpeg_read_header(&cinfo, true))
    {
        fprintf(stderr, "jpeg_read_header failed: "
            "%s\n", strerror(errno));
        return;
    }

    // 5，开始解码
    jpeg_start_decompress(&cinfo);

    // 6，获取图片的尺寸信息a（1. 需要注意的地方）
    jpginfo->width  = cinfo.output_width;
    jpginfo->height = cinfo.output_height;
    jpginfo->bpp    = cinfo.output_components*8;
    jpginfo->size   = jpginfo->width * jpginfo->height * jpginfo->bpp/8;

    int row_stride = jpginfo->width * jpginfo->bpp/8;

    // 7，根据图片的尺寸大小，分配一块相应的内存rgbdata
    //    用来存放从jpgdata解码出来的图像数据
    unsigned long linesize = cinfo.output_width * cinfo.output_components;
    unsigned long rgbsize  = linesize * cinfo.output_height;
    jpginfo->rgb = calloc(1, rgbsize); // （2. 需要注意的地方）

    // 8，循环地将图片的每一行读出并解码到rgb_buffer中
    int line = 0;
    while(cinfo.output_scanline < cinfo.output_height)
    {
        unsigned char *buffer_array[1];
        buffer_array[0] = jpginfo->rgb + cinfo.output_scanline * linesize;
        jpeg_read_scanlines(&cinfo, buffer_array, 1);
    }

    // 9，解码完了，将jpeg相关的资源释放掉
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
}


void load_jpg(const char *jpgfile, struct img_info *jpginfo)
{
    FILE *fp = fopen(jpgfile, "r");

    // 读取jpg数据
    struct stat finfo;
    stat(jpgfile, &finfo);
    char *jpgdata = calloc(1, finfo.st_size);

    // 加载JPG数据
    int n, total=0, jpgsize=finfo.st_size;
    while(jpgsize > 0)
    {
        n = fread(jpgdata+total, 1, jpgsize, fp); // n就是实际读到的字节数

        jpgsize -= n;
        total   += n;
    }
    printf("total: %d\n", total);

    // 调用jpg2rgb
    jpg2rgb(jpgdata, finfo.st_size, jpginfo);
    printf("图片尺寸：%d × %d （色深%d比特）\n", jpginfo->width,
                                                jpginfo->height,
                                                jpginfo->bpp);
    fclose(fp);
}

void show_img(struct lcd_info *lcdinfo, const char *filename)
{
    // 加载图片信息
    struct img_info *jpginfo = calloc(1, sizeof(struct img_info));
    load_jpg(filename, jpginfo);

    // 清屏
    clean_screen(lcdinfo);
    unsigned long jpg_height=jpginfo->height;
    unsigned long jpg_width=jpginfo->width;
    float scale_factor;
    // 居中指针
    if(lcdinfo->width<jpginfo->width||lcdinfo->height<jpginfo->height)
    {
        float scale_width=(float)lcdinfo->width/jpginfo->width;
        float scale_height=(float)lcdinfo->height/jpginfo->height;
        scale_factor=(scale_height<scale_width)?scale_height:scale_width;
        jpg_height=(unsigned long)(jpginfo->height*scale_factor);
        jpg_width=(unsigned long)(jpginfo->width*scale_factor);
    }
    printf("jpg_width:%d, jpg_height:%d\n",jpg_width,jpg_height);
    int x=0, y=0;
    if (lcdinfo->width > jpg_width || lcdinfo->height > jpg_height)
    {
        x = lcdinfo->width > jpg_width ? (lcdinfo->width - jpg_width) / 2 : 0;
        y = lcdinfo->height > jpg_height ? (lcdinfo->height - jpg_height) / 2 : 0;
    }
    printf("x:%d, y:%d\n",x,y);
    char *scaled_buffer = calloc(1,jpg_width * jpg_height * jpginfo->bpp/8);
    scale_image(jpginfo->rgb,jpginfo->width,jpginfo->height,scaled_buffer,jpg_width,jpg_height);
    

    char *tmp = lcdinfo->fb_mem + (800*((y>0)?(y-1):y) + x)*4;
    int wlcd = lcdinfo->width  - x*2;
    int hlcd = lcdinfo->height - y*2;


    for(int h=0; h<jpginfo->height && h<hlcd; h++)
    {
        int lcd_offset = lcdinfo->width * 4 * h;
        int rgb_offset = jpg_width * 3 * h;

        // 显示一行
        for(int w=0; w<jpginfo->width && w<wlcd; w++)
        {
            memcpy(tmp + 4*w + lcd_offset+2, scaled_buffer + 3*w + rgb_offset+0, 1);
            memcpy(tmp + 4*w + lcd_offset+1, scaled_buffer + 3*w + rgb_offset+1, 1);
            memcpy(tmp + 4*w + lcd_offset+0, scaled_buffer + 3*w + rgb_offset+2, 1);
        }
    }
}

void release_jpg(struct img_info *jpginfo)
{
    // ..
}